﻿//-----------------------------------------------------------------------
// <copyright file="UnityPerExecutionContextLifetimeManager.cs" company="deixei.com">
//     Copyright (c) deixei.com. All rights reserved.
// </copyright>
// <author>Marcio Parente</author>
//---------------------------------------------------------------------
[module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed.")]

namespace Deixei.Infrastructure
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Remoting.Messaging;
    using System.ServiceModel;
    using System.Text;
    using System.Web;
    using Deixei;
    using Deixei.Common;
    using Microsoft.Practices.Unity;
     
    /// <summary>
    /// This is a custom lifetime that preserve  instance on the same
    /// execution environment. For example, in  a WCF request or ASP.NET request, different
    /// call to resolve method return the same instance
    /// </summary>
    public class UnityPerExecutionContextLifetimeManager : LifetimeManager
    {
        #region Members

        /// <summary>
        /// Guid Key
        /// </summary>
        private Guid key;

        #endregion

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="UnityPerExecutionContextLifetimeManager" /> class.
        /// </summary>
        public UnityPerExecutionContextLifetimeManager()
            : this(Guid.NewGuid())
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="UnityPerExecutionContextLifetimeManager" /> class.
        /// </summary>
        /// <param name="key">A key for this life time manager resolver.</param>
        private UnityPerExecutionContextLifetimeManager(Guid key)
        {
            CommonUtil.CheckForEmptyGuid(key, "Key cannot be empty");
            this.key = key;
        }
        #endregion

        #region ILifetimeManager Members

        /// <summary>
        /// Retrieve a value from the backing store associated with this Lifetime policy.
        /// </summary>
        /// <returns>
        /// the object desired, or null if no such object is currently stored.
        /// </returns>
        public override object GetValue()
        {
            object result = null;

            // Get object depending on  execution environment ( WCF without HttpContext,HttpContext or CallContext)
            if (OperationContext.Current != null)
            {
                // WCF without HttpContext environment
                ContainerExtension containerExtension = OperationContext.Current.Extensions.Find<ContainerExtension>();
                if (containerExtension != null)
                {
                    result = containerExtension.Value;
                }
            }
            else if (HttpContext.Current != null)
            {
                // HttpContext available ( ASP.NET ..)
                if (HttpContext.Current.Items[this.key.ToString()] != null)
                {
                    result = HttpContext.Current.Items[this.key.ToString()];
                }
            }
            else
            {
                // Not in WCF or ASP.NET Environment, UnitTesting, WinForms, WPF etc.
                if (AppDomain.CurrentDomain.IsFullyTrusted)
                {
                    // ensure that we're in full trust
                    result = CallContext.GetData(this.key.ToString());
                }
            }

            return result;
        }

        /// <summary>
        /// Remove the given object from backing store.
        /// </summary>
        public override void RemoveValue()
        {
            if (OperationContext.Current != null)
            {
                // WCF without HttpContext environment
                ContainerExtension containerExtension = OperationContext.Current.Extensions.Find<ContainerExtension>();
                if (containerExtension != null)
                {
                    OperationContext.Current.Extensions.Remove(containerExtension);
                }
            }
            else if (HttpContext.Current != null)
            {
                // HttpContext avaiable ( ASP.NET ..)
                if (HttpContext.Current.Items[this.key.ToString()] != null)
                {
                    HttpContext.Current.Items[this.key.ToString()] = null;
                }
            }
            else
            {
                // Not in WCF or ASP.NET Environment, UnitTesting, WinForms, WPF etc.
                CallContext.FreeNamedDataSlot(this.key.ToString());
            }
        }

        /// <summary>
        /// Stores the given value into backing store for retrieval later.
        /// </summary>
        /// <param name="newValue">The object being stored.</param>
       public override void SetValue(object newValue)
        {
            if (OperationContext.Current != null)
            {
                // WCF without HttpContext environment
                ContainerExtension containerExtension = OperationContext.Current.Extensions.Find<ContainerExtension>();
                if (containerExtension == null)
                {
                    containerExtension = new ContainerExtension()
                    {
                        Value = newValue
                    };

                    OperationContext.Current.Extensions.Add(containerExtension);
                }
            }
            else if (HttpContext.Current != null)
            {
                // HttpContext avaiable ( ASP.NET ..)
                if (HttpContext.Current.Items[this.key.ToString()] == null)
                {
                    HttpContext.Current.Items[this.key.ToString()] = newValue;
                }
            }
            else
            {
                // Not in WCF or ASP.NET Environment, UnitTesting, WinForms, WPF etc.
                if (AppDomain.CurrentDomain.IsFullyTrusted)
                {
                    // ensure that we're in full trust
                    CallContext.SetData(this.key.ToString(), newValue);
                }
            }
        }

        #endregion

        #region Nested

        /// <summary>
        /// Custom extension for OperationContext scope
        /// </summary>
        private class ContainerExtension : IExtension<OperationContext>
        {
            #region Members

            /// <summary>
            /// Gets or sets the value.
            /// </summary>
            /// <value>
            /// The value.
            /// </value>
            public object Value { get; set; }

            #endregion

            #region IExtension<OperationContext> Members

            /// <summary>
            /// Attach Operation context
            /// </summary>
            /// <param name="owner">Operation context</param>
            public void Attach(OperationContext owner)
            {
            }

            /// <summary>
            /// Detach Operation context
            /// </summary>
            /// <param name="owner">Operation context</param>
            public void Detach(OperationContext owner)
            {
            }

            #endregion
        }
        #endregion
    }
}
